#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>                                 //编译时需要添加参数 -lpthread ，否则会报错

#define SPEED                   50                   //延时种子,SPEED值越大过程越慢
#define IS_PLAY_BGM             1                    //Do you play background music? The end after have bug.I don't know why?

char *strrpc(char *str, char *oldstr, char *newstr); //字符串替换函数


#if IS_PLAY_BGM
void *play_bgm(void * arg){system("./madplay bgm.mp3");}//好像需要sudo apt-get install madplay
#endif

typedef struct
{
    unsigned int number;                            //成员编号
    char name[128];                                 //姓名
}name;


/***************************************************
 * 读取文本文件行数，返回行数
 ***************************************************/
unsigned int get_linecount_totxt(FILE *fp)
{
    unsigned int count = 0;
    char buf[128] = {0};
    while(fgets(buf, sizeof(buf), fp))              //每次读取一行数据，直到读取失败。
    {
        count++;
        //printf("%s", buf);                        //打印该行。
    }
    rewind(fp);
    return count;
}


/****************************************************
 * fp-文件指针、classone-name数组、行数
 * 获取每行的姓名并添加到数组当中，返回 name classone[]
 ****************************************************/
name *getline_name(FILE *fp, name classone[])
{
    char buf[128] = {0};
    unsigned int i = 0, every_strlen = 0;
    name *one_p = classone;
    while(fgets(buf, sizeof(buf), fp))              //每次读取一行数据，直到读取文件末尾。
    {

        every_strlen = strlen(buf);                 //获取名字的长度
        buf[every_strlen - 1] = '\0';               //去除fgets函数中的换行值

        bzero(one_p->name, sizeof(one_p->name));

        one_p->number = i;                          //设置编号
        strcpy(one_p->name, buf);                   //将姓名写入到结构体变量中

        i++;
        one_p++;
    }

    return classone;
}


/****************************************************
 * 给.txt文件随机点名 *fp文件指针 count指定名单的个数
 ****************************************************/
void runto_txt(FILE *fp, unsigned int count)
{
    int i,j;
    unsigned int line_count = get_linecount_totxt(fp);//文件行号
    name classone[line_count];                      //根据行号定义数组大小

    //printf("Line count is %d.\n", line_count);      //测试打印行数。

    getline_name(fp, classone);                     //获取每行的姓名

    /*for(i = 0; i < (int)line_count; i++)            //测试遍历成功
        printf("classone[%d] = %s\n", i, classone[i].name);*/


    //新建结构体数组成功，开始延时遍历
    //函数原型y=x^2，y为延时时间

    srand((unsigned int)time(NULL));                //生成随机数种子

    for(j = 0; j < (int)count; j++)
    {
        for(i = 0; i < SPEED; i++)
        {
            //输出姓名
            printf("\r\33[K%s", classone[rand()%(line_count - 1) + 1].name);
            fflush(stdout);                             //刷新缓冲区
            usleep(i*i*100);                            //延时
        }
        printf("\n");
    }
}


/******************************************************
 * 针对.csv文件，以.csv文本文件打开，然后按行读取
*******************************************************/
name *getline_name_forxls(FILE *fp, name classone[])
{
    char buf[128] = {0};
    unsigned int i = 0;
    name *one_p = classone;
    while(fgets(buf, sizeof(buf), fp))              //每次读取一行数据，直到读取文件末尾。
    {
        if(i == 0)                                  //去掉第一行无用的内容
        {
            i++;
            continue;
        }

        bzero(one_p->name, sizeof(one_p->name));

        one_p->number = i;                          //设置编号
        sscanf(buf, "\"\";\"\";\"%s\"", one_p->name);//将buf中的内容格式化输入到name中
        one_p->name[strlen(one_p->name) - 1] = '\0';//不知为啥最后读到一个'"'，故删掉

        i++;
        one_p++;
    }

    return classone;
}


/***************************************************************
 * 传入.xls的文件名，并调用./xls2csv 库进行转换，完成名单获取后删除程序产物
*****************************************************************/
void runto_xls(char *filename, unsigned int count)
{
    /*由外链接库进行转换，要转化成csv格式的文本文件*/

    unsigned int cmd_strlen, cmd_str2len, filename_strlen = strlen(filename);

    char newfilename[filename_strlen + 1];
    strcpy(newfilename, filename);
    strrpc(newfilename, ".xls", ".csv");                        //设置成.csv新文件名

    //./xls2csv namelist.xls > namelist.csv
    cmd_strlen = strlen("./xls2csv  > ") + 2*filename_strlen + 1;//计算命令字符长度
    char *cmd_string = (char *)calloc(cmd_strlen, sizeof(char));//申请空间
    cmd_str2len = strlen("rm ") + filename_strlen + 1;          //计算删除临时文件命令长度
    char *cmd_rm = (char *)calloc(cmd_str2len, sizeof(char));   //用于删除临时产生的.csv文件

    //开始追加命令字符串
    strcat(cmd_string, "./xls2csv ");
    strcat(cmd_string, filename);
    strcat(cmd_string, " > ");
    strcat(cmd_string, newfilename);
    system(cmd_string);                              //将.xls文件转换成.csv文本格式
    free(cmd_string);
    cmd_string = NULL;


    //转换完成后开始按照txt格式思路逐行读取文件
    FILE *fp = fopen(newfilename, "r");

    if(fp == NULL)
    {
        printf("\033[1m\033[31mFailed to open file to %s.\033[0m\n", newfilename);
        exit(1);
    }

    int i,j;
    unsigned int line_count = get_linecount_totxt(fp) - 1;//文件行数
    name classone[line_count];                      //根据行号定义数组大小

    getline_name_forxls(fp, classone);              //获取名单

    fclose(fp);                                     //获取完毕后立即关闭文件
    strcat(cmd_rm, "rm ");                          //开始拼接删除文件的命令
    strcat(cmd_rm, newfilename);
    system(cmd_rm);                                 //删除.csv文件
    free(cmd_rm);
    cmd_rm = NULL;

    //新建结构体数组成功，开始延时遍历
    //函数原型y=x^2，y为延时时间

    srand((unsigned int)time(NULL));                //生成随机数种子

    for(j = 0; j < (int)count; j++)
    {
        for(i = 0; i < SPEED; i++)
        {
            //输出姓名
            printf("\r\33[K%s", classone[rand()%(line_count - 1) + 1].name);
            fflush(stdout);                          //刷新缓冲区
            usleep(i*i*100);                         //延时
        }
        printf("\n");
    }
}


/*
 * 命令行参数格式为 ./ramdon_select_name <文件名> [指定人数]
*/
int main(int argc, char *argv[])
{
    if(argc < 2 || argc > 3)                         //过滤无效参数
    {
        printf("\033[1m\033[31mOnly 1 or 2 parameters can be passed in.\033[0m\n");
        return 0;
    }

    FILE *fp;

    fp = fopen(argv[1], "r");                        //打开文件
    if(fp == NULL)
    {
        printf("\033[1m\033[31mFailed to open file or file does not exist.\033[0m\n");
        return 0;
    }

#if IS_PLAY_BGM
    //**************************************
    pthread_t play_bgm_thread;
    pthread_create(&play_bgm_thread, NULL, play_bgm, NULL);
    usleep(100 * 1000);
    system("clear");
    //**************************************
#endif

    int count = 1;                                  //设置默认选择人数
    if(argc == 3)
        count = atoi(argv[2]);                      //将第3个参数转换为数字

    if(strstr(argv[1], ".xls") != NULL)             //过滤.xls文件
        runto_xls(argv[1], count);
    else
        runto_txt(fp, count);                       //文本文件默认不过滤

    printf("Selecting the end.\n");

    fclose(fp);                                     //关闭文件

#if IS_PLAY_BGM
    system("killall -9 madplay");                   //利用system函数调用killall命令将madplay终止掉
    pthread_join(play_bgm_thread, NULL);            //等待，避免死锁，结束后好像有一个很大的问题
#endif

    return 0;
}









































/* 功  能：将str字符串中的oldstr字符串替换为newstr字符串
* 参  数：str：操作目标 oldstr：被替换者 newstr：替换者
* 返回值：返回替换之后的字符串
* 版  本： V0.2
*/
char *strrpc(char *str, char *oldstr, char *newstr){
    unsigned int str_lenth = strlen(str);
    char *bstr = calloc(str_lenth, 1);//转换缓冲区
    memset(bstr, 0, str_lenth);

    for (unsigned int i = 0; i < strlen(str); i++){
        if (!strncmp(str + i, oldstr, strlen(oldstr))){//查找目标字符串
            strcat(bstr, newstr);
            i += strlen(oldstr) - 1;
        }
        else{
            strncat(bstr, str + i, 1);//保存一字节进缓冲区
        }
    }

    strcpy(str, bstr);
    return str;
}
